package com.bkck.sharkblockade.levels.paddle;

import com.bkck.sharkblockade.jellyfish.Jellyfish;
import com.bkck.sharkblockade.levels.lines.Line;
import com.bkck.sharkblockade.levels.lines.Point;
import com.bkck.sharkblockade.levels.thread.MainGamePanel;
import android.graphics.Bitmap;
import android.graphics.Canvas;

/**
 * The Paddle class.
 * 
 * @author Team BKCK
 */
public class Paddle {

  // paddle image
  private Bitmap bitmap;

  // paddle dimension
  private int paddleWidth;
  private int paddleHeight;

  // paddle coordinate..(x, y) is top left corner of this image
  private int x;
  public int y;
  private float previousX;

  // whether it's touched currently by the player.
  private boolean touched;

  private boolean stunned;
  private long stunnedTime;

  private Line surface;
  private Line bottom;
  public Line leftEdge;
  public Line rightEdge;

  /**
   * The constructor.
   * 
   * @param bitmap The bitmap image.
   * @param x The x coordinate.
   * @param touched Whether the image is being touched.
   * @param velocity The velocity of the paddle.
   */
  public Paddle(Bitmap bitmap, int x, int y) {
    this.bitmap = bitmap;
    this.paddleHeight = bitmap.getHeight();
    this.paddleWidth = bitmap.getWidth();
    this.x = x;
    this.y = y;
    this.touched = false;
    this.stunned = false;
    surface = new Line(new Point(x, y), new Point(x + paddleWidth, y));
    bottom = new Line(new Point(x, y + paddleHeight), new Point(x + paddleWidth, y + paddleHeight));
    leftEdge = new Line(new Point(x, y), new Point(x, y + paddleHeight));
    rightEdge =
        new Line(new Point(x + paddleWidth, y), new Point(x + paddleWidth, y + paddleHeight));
  }

  /**
   * Empty constructor to be called by Activities.
   */
  public Paddle() {

  }

  /**
   * Set the paddle image.
   * 
   * @param bitmap
   */
  public void setBitmap(Bitmap bitmap) {
    this.bitmap = bitmap;
    this.paddleHeight = bitmap.getHeight();
    this.paddleWidth = bitmap.getWidth();
  }

  /**
   * Return the paddle image.
   * 
   * @return The Bitmap.
   */
  public Bitmap getBitmap() {
    return bitmap;
  }

  /**
   * Set this paddle's x coordinate.
   * 
   * @param x coordinate
   */
  public void setX(int x) {
    previousX = x;
    this.x = x;
    updateEdges(x);
  }

  /**
   * Return this paddle's x coordinate.
   * 
   * @return x coordinate
   */
  public int getX() {
    return x;
  }

  public void setPreviousX(float previousX) {
    this.previousX = previousX;
  }

  public float getPreviousX() {
    return previousX;
  }

  /**
   * Return this paddle's y coordinate.
   * 
   * @return y coordinate
   */
  public int getY() {
    return y;
  }

  /**
   * Set whether this paddle is touched.
   * 
   * @param touched
   */
  public void setTouched(boolean touched) {
    this.touched = touched;
  }

  /**
   * Return whether this paddle is touched.
   * 
   * @return
   */
  public boolean isTouched() {
    return touched;
  }

  /**
   * Return the paddle width.
   * 
   * @return The width in pixels.
   */
  public int getWidth() {
    return paddleWidth;
  }

  /**
   * Return the paddle height.
   * 
   * @return The height in pixels.
   */
  public int getHeight() {
    return paddleHeight;
  }

  public boolean getStunned() {
    return stunned;
  }

  public void setStunned(boolean stunned) {
    this.stunned = stunned;
  }

  public void stunned() {
    stunned = true;
    setBitmap(MainGamePanel.paddleStunned);
    stunnedTime = System.currentTimeMillis();
  }

  /**
   * Handles the actions performed by the player.
   * 
   * @param eventX x coordinate of the event
   * @param eventY y coordinate of the event.
   * @param screenWidth The width of the screen.
   */
  public void handleActionDown(int eventX, int eventY, int screenWidth) {

    if (!stunned) {
      // allow player to touch anywhere below this paddle to move it.
      if (eventY >= y) {
        // touched
        setTouched(true);
        previousX = x;
        // don't want the paddle to stick out to the left boundary
        if (eventX <= bitmap.getWidth() / 2) {
          x = 0;
        }
        // don't want the paddle to stick out to the right boundary
        else if (eventX >= screenWidth - bitmap.getWidth() / 2) {
          x = screenWidth - bitmap.getWidth();
        }
        else {
          x = eventX - bitmap.getWidth() / 2;
        }
        updateEdges(x);

      }
      else {
        setTouched(false);
      }
    }

  }

  /**
   * x for (x,y), the top left coordinate of the paddle.
   * 
   * @param x
   */
  public void updateEdges(float x) {
    surface.getPoint1().setX(x);
    surface.getPoint2().setX(x + paddleWidth);
    bottom.getPoint1().setX(x);
    bottom.getPoint2().setX(x + paddleWidth);
    leftEdge.getPoint1().setX(x);
    leftEdge.getPoint2().setX(x);
    rightEdge.getPoint1().setX(x + paddleWidth);
    rightEdge.getPoint2().setX(x + paddleWidth);
  }

  public boolean collideWith(Jellyfish jellyfish) {

    Point intersection = null;

    // intersection between top of paddle and jellyfish bottom left
    intersection =
        findIntersection(surface, jellyfish.getX(),
            jellyfish.getPreviousY() + jellyfish.getHeight(), jellyfish.getX(), jellyfish.getY()
                + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    // intersection between top of paddle and jellyfish bottom right
    intersection =
        findIntersection(surface, jellyfish.getX() + jellyfish.getWidth(), jellyfish.getPreviousY()
            + jellyfish.getHeight(), jellyfish.getX() + jellyfish.getWidth(), jellyfish.getY()
            + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    // intersection between bottom of paddle and jellyfish top left
    intersection =
        findIntersection(bottom, jellyfish.getX(), jellyfish.getPreviousY(), jellyfish.getX(),
            jellyfish.getY());
    if (intersection != null) {
      return true;
    }

    // intersection between bottom of paddle and jellyfish top right
    intersection =
        findIntersection(bottom, jellyfish.getX() + jellyfish.getWidth(), jellyfish.getPreviousY(),
            jellyfish.getX() + jellyfish.getWidth(), jellyfish.getY());
    if (intersection != null) {
      return true;
    }

    // intersection between left of paddle and jellyfish top right
    intersection =
        findIntersection(leftEdge, jellyfish.getX() + jellyfish.getWidth(),
            jellyfish.getPreviousY(), jellyfish.getX() + jellyfish.getWidth(), jellyfish.getY());
    if (intersection != null) {
      return true;
    }

    // intersection between left of paddle and jellyfish bottom right
    intersection =
        findIntersection(leftEdge, jellyfish.getX() + jellyfish.getWidth(),
            jellyfish.getPreviousY() + jellyfish.getHeight(),
            jellyfish.getX() + jellyfish.getWidth(), jellyfish.getY() + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    // intersection between right of paddle and jellyfish top left
    intersection =
        findIntersection(rightEdge, jellyfish.getX(), jellyfish.getPreviousY(), jellyfish.getX(),
            jellyfish.getY());
    if (intersection != null) {
      return true;
    }

    // intersection between right of paddle and jellyfish bottom left
    intersection =
        findIntersection(rightEdge, jellyfish.getX(),
            jellyfish.getPreviousY() + jellyfish.getHeight(), jellyfish.getX(), jellyfish.getY()
                + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    // intersection between top of paddle and left of jellyfish
    intersection =
        findIntersection(surface, jellyfish.getX(), jellyfish.getPreviousY(), jellyfish.getX(),
            jellyfish.getY() + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    // intersection between top of paddle and right of jellyfish
    intersection =
        findIntersection(surface, jellyfish.getX() + jellyfish.getWidth(), jellyfish.getPreviousY(),
            jellyfish.getX() + jellyfish.getWidth(), jellyfish.getY() + jellyfish.getHeight());
    if (intersection != null) {
      return true;
    }

    return false;
  }

  /**
   * Find the intersection of two line segments where second line is represented by two points. (cx,
   * cy) and (dx, dy)
   * 
   * @param line
   * @param cx
   * @param cy
   * @param dx
   * @param dy
   * @return
   */
  public Point findIntersection(Line line, float cx, float cy, float dx, float dy) {
    return findIntersection(line.getPoint1().getX(), line.getPoint1().getY(), line.getPoint2()
        .getX(), line.getPoint2().getY(), cx, cy, dx, dy);
  }

  /**
   * Find the intersection of two line segments. If there's no intersection a null is returned. a,
   * b, c, d are points. Line 1 = (a, b) and Line 2 = (c, d) Source:
   * http://goosebumps4all.net/34all/bb/showthread.php?tid=113
   * 
   * @param ax
   * @param ay
   * @param bx
   * @param by
   * @param cx
   * @param cy
   * @param dx
   * @param dy
   * @return
   */
  public Point findIntersection(float ax, float ay, float bx, float by, float cx, float cy,
      float dx, float dy) {
    float distAB;
    float sinValue;
    float cosValue;
    float newX;
    float posAB;

    // return null if either line has length 0
    if ((ax == bx && ay == by) || (cx == dx && cy == dy)) {
      return null;
    }
    // if the end of the lines intersect
    if (ax == cx && ay == cy || bx == cx && by == cy) {
      return new Point(cx, cy);
    }
    if (ax == dx && ay == dy || bx == dx && by == dy) {
      return new Point(dx, dy);
    }

    // (1) Translate the system so that point A is on the origin.
    bx -= ax;
    by -= ay;
    cx -= ax;
    cy -= ay;
    dx -= ax;
    dy -= ay;

    // Discover the length of segment A-B.
    distAB = (float) Math.sqrt(bx * bx + by * by);

    // (2) Rotate the system so that point B is on the positive X axis.
    cosValue = bx / distAB;
    sinValue = by / distAB;
    newX = cx * cosValue + cy * sinValue;
    cy = cy * cosValue - cx * sinValue;
    cx = newX;
    newX = dx * cosValue + dy * sinValue;
    dy = dy * cosValue - dx * sinValue;
    dx = newX;

    // Fail if segment C-D doesn't cross line A-B.
    if ((cy < 0. && dy < 0.) || (cy >= 0. && dy >= 0.)) {
      return null;
    }

    // (3) Discover the position of the intersection point along line A-B.
    posAB = dx + (cx - dx) * dy / (dy - cy);

    // Fail if segment C-D crosses line A-B outside of segment A-B.
    if (posAB < 0. || posAB > distAB) {
      return null;
    }

    // (4) Apply the discovered position to line A-B in the original coordinate system.
    return new Point(ax + posAB * cosValue, ay + posAB * sinValue);
  }

  /**
   * Draw the paddle on the canvas.
   * 
   * @param canvas
   */
  public void draw(Canvas canvas) {
    canvas.drawBitmap(bitmap, x, y, null);
  }

  public Line getSurfaceLine() {
    return surface;
  }

  public long getStunnedTime() {
    return stunnedTime;
  }
}
